package nsu.edu.zsq.service.impl.sys;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import nsu.edu.zsq.bean.ExamStudent;
import nsu.edu.zsq.bean.ExamStudentExample;
import nsu.edu.zsq.bean.ExamTeacher;
import nsu.edu.zsq.bean.ExamTeacherExample;
import nsu.edu.zsq.bean.SchExam;
import nsu.edu.zsq.bean.SchExamExample;
import nsu.edu.zsq.bean.SchMajor;
import nsu.edu.zsq.bean.SchMajorExample;
import nsu.edu.zsq.bean.SchRoomExample;
import nsu.edu.zsq.bean.StuCourse;
import nsu.edu.zsq.bean.StuCourseExample;
import nsu.edu.zsq.bean.Student;
import nsu.edu.zsq.bean.StudentExample;
import nsu.edu.zsq.bean.Teacher;
import nsu.edu.zsq.common.RequestHolder;
import nsu.edu.zsq.dao.ExamStudentMapper;
import nsu.edu.zsq.dao.ExamTeacherMapper;
import nsu.edu.zsq.dao.SchExamMapper;
import nsu.edu.zsq.dao.SchMajorMapper;
import nsu.edu.zsq.dao.SchRoomMapper;
import nsu.edu.zsq.dao.StuCourseMapper;
import nsu.edu.zsq.dao.StudentMapper;
import nsu.edu.zsq.exception.ParamException;
import nsu.edu.zsq.param.PageQuery;
import nsu.edu.zsq.service.sys.SchExamService;
import nsu.edu.zsq.service.sys.SysLogService;
import nsu.edu.zsq.service.sys.TeacherService;

/** 
* Description: 考试的service实现类<br>
*/
@Service
public class SchExamServiceImpl implements SchExamService {
    
    @Autowired
    private SchExamMapper schExamMapper;
    @Autowired
    private StuCourseMapper stuCourseMapper;
    @Autowired
    private SysLogService sysLogService;
    @Autowired
    private ExamTeacherMapper examTeacherMapper;
    @Autowired
    private ExamStudentMapper examStudentMapper;
    @Autowired
    private SchRoomMapper schRoomMapper;
    @Autowired
    private SchMajorMapper schMajorMapper;
    @Autowired
    private TeacherService teacherService;
    @Autowired
    private StudentMapper studentMapper;

    @Override
    public SchExam getSchExam(int schExamId) {
        SchExam schExam = schExamMapper.selectByPrimaryKey(schExamId);
        return schExam;
    }
    
    @Override
    public PageInfo<SchExam> listSchExamByMajorNoWithPage(String majorNo, PageQuery pageQuery) {
        PageHelper.startPage(pageQuery.getPageNum(),pageQuery.getPageSize());
        List<SchExam> listTchExam = schExamMapper.listSchExamByMajorNo(majorNo);
        PageInfo<SchExam> pageInfo = new PageInfo<SchExam>(listTchExam, pageQuery.getNavigatePages());
        return pageInfo;
    }

    @Override
    public List<HashMap<String, String>> listStuCourseByMajorNo(String majorNo) {
        List<HashMap<String, String>> listStuCourse = stuCourseMapper.listStuCourseByMajorNo(majorNo);
        return listStuCourse;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveCourseToSchExam(String[] courseNos, String majorNo) {
        List<SchExam> after = new ArrayList<SchExam>();
        for (String courseNo : courseNos) {
            SchExam schExam = SchExam.builder().courseNo(courseNo).majorNo(majorNo)
                    .operator(RequestHolder.getCurrentSysUser().getAccount())
                    .operatorTime(new Date()).build();
            after.add(schExam);
            // 验证要添加的课程是否已经存在
            if (checkCourseExist(courseNo)) {
                throw new ParamException("该课程已经存在");
            }
            schExamMapper.insertSelective(schExam);
        }

        // 添加日志
        String instruction = "考试安排-添加考试课程";
        sysLogService.saveSysLogBySysUser(null, after, instruction);
    }

    /**
     * 检查要添加的考试课程是否已经存在
     * @param courseNo 课程代码
     * @return boolean true：存在 false：不存在
     */
    private boolean checkCourseExist(String courseNo) {
        SchExamExample example = new SchExamExample();
        example.createCriteria().andCourseNoEqualTo(courseNo);
        return schExamMapper.countByExample(example) > 0;
    }
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteCourseFromSchExam(int[] schExamIds) {
        List<SchExam> before = new ArrayList<SchExam>();
        for (int schExamId : schExamIds) {
            SchExam beforeTchExam = getSchExam(schExamId);
            before.add(beforeTchExam);
            // 1.删除考试中的课程
            schExamMapper.deleteByPrimaryKey(schExamId);
            // 2.删除与该考试课程相关的监考教师信息
            ExamTeacherExample teacherExample = new ExamTeacherExample();
            teacherExample.createCriteria().andCourseNoEqualTo(beforeTchExam.getCourseNo());
            examTeacherMapper.deleteByExample(teacherExample);
            // 3.删除与该考试课程相关的学生信息
            ExamStudentExample studentExample = new ExamStudentExample();
            studentExample.createCriteria().andCourseNoEqualTo(beforeTchExam.getCourseNo());
            examStudentMapper.deleteByExample(studentExample);
        }
        
        // 添加日志
        String instruction = "考试安排-删除考试课程";
        sysLogService.saveSysLogBySysUser(before, null, instruction);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateSchExam(SchExam schExam) {
        SchExam before = getSchExam(schExam.getId());
        schExam.setOperator(RequestHolder.getCurrentSysUser().getAccount());
        schExam.setOperatorTime(new Date());
        schExamMapper.updateByPrimaryKeySelective(schExam);
        
        // 添加日志
        String instruction = "考试安排-时间设置";
        sysLogService.saveSysLogBySysUser(before, schExam, instruction);
    }

    
    @Override
    public Map<String, Object> getSchExamWithRoom(int schExamId) {
        Map<String, Object> map = new HashMap<String, Object>(2);
        SchExam schExam = schExamMapper.getSchExam(schExamId);
        // 获取以;分隔的所有教室名称
        String allRoom = schExam.getRoomName();
        String[] roomNames = null;
        if (allRoom != null && allRoom.length() > 0) {
            roomNames = allRoom.split(";");
        }
        map.put("schExam", schExam);
        map.put("roomNames", roomNames);
        return map;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveExamRoom(SchExam schExam) {
        // 1.检查教室是否存在
        if (!checkRoomNameExist(schExam.getRoomName())) {
            throw new ParamException("该教室不存在，请重新输入");
        }
        SchExam beforeSchExam = getSchExam(schExam.getId());
        // 检查教室时间冲突
        if (checkExamTimeConflict(beforeSchExam, schExam.getRoomName())) {
            throw new ParamException("教室考试时间安排冲突");
        }
        // 2.拼接考试教室
        StringBuilder sb = new StringBuilder();
        if (beforeSchExam.getRoomName() != null && beforeSchExam.getRoomName().length() > 0) {
            sb.append(beforeSchExam.getRoomName());
            sb.append(";");
        }
        sb.append(schExam.getRoomName());
        schExam.setRoomName(sb.toString());
        schExam.setOperator(RequestHolder.getCurrentSysUser().getAccount());
        schExam.setOperatorTime(new Date());
        SchExam before = SchExam.builder().id(beforeSchExam.getId()).roomName(beforeSchExam.getRoomName())
                .operator(RequestHolder.getCurrentSysUser().getAccount())
                .operatorTime(new Date()).build();
        schExamMapper.updateByPrimaryKeySelective(schExam);
        
        // 添加日志
        String instruction = "考试安排-考试安排-添加考试教室";
        sysLogService.saveSysLogBySysUser(before, schExam, instruction);
    }

    /**
     * 检查教室是否存在
     * @param roomName 教室名称
     * @return boolean true：存在 false：不存在
     */
    private boolean checkRoomNameExist(String roomName) {
        SchRoomExample example = new SchRoomExample();
        example.createCriteria().andRoomNameEqualTo(roomName);
        return schRoomMapper.countByExample(example) > 0;
    }

    /**
     * 检查是否教室考试时间冲突
     * @param schExam 考试课程信息
     * @param roomName 考试教室
     * @return boolean true：冲突 false：不冲突
     */
    private boolean checkExamTimeConflict(SchExam schExam, String roomName) {
        SchExamExample example = new SchExamExample();
        example.createCriteria().andExamDateEqualTo(schExam.getExamDate())
            .andExamTimeEqualTo(schExam.getExamTime());
        List<SchExam> listSchExam = schExamMapper.selectByExample(example);
        for (SchExam schExam2 : listSchExam) {
            String allRoom = schExam2.getRoomName();
            String[] roomNames = new String[] {};
            if (allRoom != null && allRoom.length() > 0) {
                roomNames = allRoom.split(";");
            }
            for (String string : roomNames) {
                if (string.equals(roomName)) {
                    return true;
                }
            }
        }
        return false;
    }
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delExamRoom(int schExamId, String[] roomNames) {
        SchExam beforeSchExam = getSchExam(schExamId);
        SchExam before = SchExam.builder().id(beforeSchExam.getId()).roomName(beforeSchExam.getRoomName())
                .operator(beforeSchExam.getOperator())
                .operatorTime(beforeSchExam.getOperatorTime()).build();
        // 拼接新的考试教室
        StringBuilder sb = new StringBuilder();
        if (beforeSchExam.getRoomName() != null && beforeSchExam.getRoomName().length() > 0) {
            String[] beforeRoomNames = beforeSchExam.getRoomName().split(";");
            for (String beforeRoomName : beforeRoomNames) {
                boolean flag = true;
                for (String roomName : roomNames) {
                    if (beforeRoomName.equals(roomName)) {
                        flag = false;
                        break;
                    }
                }
                if (flag) {
                    sb.append(beforeRoomName);
                    sb.append(";");
                }
            }
        } else {
            return;
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        SchExam after = SchExam.builder().id(beforeSchExam.getId()).roomName(sb.toString())
                .operator(RequestHolder.getCurrentSysUser().getAccount())
                .operatorTime(new Date()).build();
        // 删除考试教室
        schExamMapper.updateByPrimaryKeySelective(after);
        
        List<String> listRoomName = new ArrayList<String>();
        for (String roomName : roomNames) {
            listRoomName.add(roomName);
        }
        // 删除与该考试课程相关的监考教师信息
        ExamTeacherExample teacherExample = new ExamTeacherExample();
        teacherExample.createCriteria().andExamIdEqualTo(schExamId).andRoomNameIn(listRoomName);
        examTeacherMapper.deleteByExample(teacherExample);
        // 删除与该考试课程相关的学生信息
        ExamStudentExample studentExample = new ExamStudentExample();
        studentExample.createCriteria().andExamIdEqualTo(schExamId).andRoomNameIn(listRoomName);
        examStudentMapper.deleteByExample(studentExample);
        
        // 添加日志
        String instruction = "考试安排-考试安排-删除考试教室";
        sysLogService.saveSysLogBySysUser(before, after, instruction);
    }

    @Override
    public List<ExamTeacher> listExamTeacher(SchExam schExam) {
        List<ExamTeacher> listExamTeacher = examTeacherMapper.listExamTeacher(schExam);
        return listExamTeacher;
    }

    @Override
    public PageInfo<Teacher> listTeacherByMajorNoWithPage(String majorNo, PageQuery pageQuery) {
        SchMajorExample example = new SchMajorExample();
        example.createCriteria().andMajorNoEqualTo(majorNo);
        List<SchMajor> listSchMajor = schMajorMapper.selectByExample(example);
        PageInfo<Teacher> listTeacherWithPage = null;
        if (listSchMajor.size() > 0) {
            listTeacherWithPage = teacherService.listTeacherWithPage(listSchMajor.get(0).getOrgId(), pageQuery);
        }
        return listTeacherWithPage;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveTeacherToExam(ExamTeacher examTeacher, String[] teacherNos) {
        List<ExamTeacher> after = new ArrayList<ExamTeacher>();
        SchExam schExam = getSchExam(examTeacher.getExamId());
        examTeacher.setExamDate(schExam.getExamDate());
        examTeacher.setExamTime(schExam.getExamTime());
        examTeacher.setCourseNo(schExam.getCourseNo());
        for (String teacherNo : teacherNos) {
            examTeacher.setTeacherNo(teacherNo);
            after.add(examTeacher);
            // 检查该教师是否已经存在
            if (checkExamTeacherExist(examTeacher)) {
                throw new ParamException("该监考教师已经存在");
            }
            examTeacherMapper.insertSelective(examTeacher);
        }
        
        // 添加日志
        String instruction = "考试安排-监考教师-添加监考教师";
        sysLogService.saveSysLogBySysUser(null, after, instruction);
    }
    
    /**
     * 检查在一个考试课程中是否已经存在该监考教师
     * @param examTeacher 监考教师信息
     * @return boolean true：存在 false：不存在
     */
    private boolean checkExamTeacherExist(ExamTeacher examTeacher) {
        ExamTeacherExample example = new ExamTeacherExample();
        example.createCriteria().andExamIdEqualTo(examTeacher.getExamId()).andTeacherNoEqualTo(examTeacher.getTeacherNo());
        return examTeacherMapper.countByExample(example) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delExamTeacher(int[] examTeacherIds) {
        List<ExamTeacher> before = new ArrayList<ExamTeacher>();
        for (int examTeacherId : examTeacherIds) {
            ExamTeacher beforeExamTeacher = examTeacherMapper.selectByPrimaryKey(examTeacherId);
            before.add(beforeExamTeacher);
            examTeacherMapper.deleteByPrimaryKey(examTeacherId);
        }
        
        // 添加日志
        String instruction = "考试安排-监考教师-删除监考教师";
        sysLogService.saveSysLogBySysUser(before, null, instruction);
    }

    @Override
    public List<ExamStudent> listExamStudent(SchExam schExam) {
        List<ExamStudent> listExamStudent = examStudentMapper.listExamStudent(schExam);
        return listExamStudent;
    }

    @Override
    public PageInfo<Student> listStudentByMajorNoWithPage(String courseNo, PageQuery pageQuery) {
        // 获取课程所在班级id
        StuCourseExample example = new StuCourseExample();
        example.createCriteria().andCourseNoEqualTo(courseNo);
        List<StuCourse> listStuCourse = stuCourseMapper.selectByExample(example);
        List<Integer> classIds = new ArrayList<Integer>();
        for (StuCourse stuCourse : listStuCourse) {
            classIds.add(stuCourse.getClassId());
        }
        // 考试课程中已经存在的考生
        ExamStudentExample examStudentExample = new ExamStudentExample();
        examStudentExample.createCriteria().andCourseNoEqualTo(courseNo);
        List<ExamStudent> listExamStudentByCourseNo = examStudentMapper.selectByExample(examStudentExample);
        List<String> stuNos = new ArrayList<String>();
        for (ExamStudent examStudent : listExamStudentByCourseNo) {
            stuNos.add(examStudent.getStuNo());
        }
        // 根据班级id，获取所有该课程下的学生（并排除已经存在的考生）
        PageHelper.startPage(pageQuery.getPageNum(),pageQuery.getPageSize());
        StudentExample studentExample = new StudentExample();
        studentExample.createCriteria().andClassIdIn(classIds);
        studentExample.createCriteria().andStuNoNotIn(stuNos);
        List<Student> listStudent = studentMapper.selectByExample(studentExample);
        PageInfo<Student> pageInfo = new PageInfo<Student>(listStudent, pageQuery.getNavigatePages());
        return pageInfo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveStudentToExam(ExamStudent examStudent, String[] stuNos) {
        List<ExamStudent> after = new ArrayList<ExamStudent>();
        SchExam schExam = getSchExam(examStudent.getExamId());
        examStudent.setExamDate(schExam.getExamDate());
        examStudent.setExamTime(schExam.getExamTime());
        examStudent.setCourseNo(schExam.getCourseNo());
        for (String stuNo : stuNos) {
            examStudent.setStuNo(stuNo);
            after.add(examStudent);
            // 检查该学生是否已经存在
            if (checkExamStudentExist(examStudent)) {
                throw new ParamException("考场已经存在该学生");
            }
            examStudentMapper.insertSelective(examStudent);
        }
        
        // 添加日志
        String instruction = "考试安排-考试学生-添加考试学生";
        sysLogService.saveSysLogBySysUser(null, after, instruction);
        
    }
    
    /**
     * 检查考试课程中是否已经存在该考生
     * @param examStudent 考试学生信息
     * @return boolean true：存在 false：不存在
     */
    private boolean checkExamStudentExist(ExamStudent examStudent) {
        ExamStudentExample example = new ExamStudentExample();
        example.createCriteria().andExamIdEqualTo(examStudent.getExamId()).andStuNoEqualTo(examStudent.getStuNo());
        return examStudentMapper.countByExample(example) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delExamStudent(int[] examStudentIds) {
        List<ExamStudent> before = new ArrayList<ExamStudent>();
        for (int examStudentId : examStudentIds) {
            ExamStudent beforeExamStudent = examStudentMapper.selectByPrimaryKey(examStudentId);
            before.add(beforeExamStudent);
            examStudentMapper.deleteByPrimaryKey(examStudentId);
        }
        
        // 添加日志
        String instruction = "考试安排-考试学生-删除考试学生";
        sysLogService.saveSysLogBySysUser(before, null, instruction);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void autoSeatNumber(ExamStudent examStudent) {
        ExamStudentExample examStudentExample = new ExamStudentExample();
        examStudentExample.createCriteria().andExamIdEqualTo(examStudent.getExamId()).andRoomNameEqualTo(examStudent.getRoomName());
        examStudentExample.setOrderByClause("stu_no asc");
        List<ExamStudent> listExamStudent = examStudentMapper.selectByExample(examStudentExample);
        List<ExamStudent> before = new ArrayList<ExamStudent>();
        List<ExamStudent> after = new ArrayList<ExamStudent>();
        for (int i = 0; i < listExamStudent.size(); i++) {
            ExamStudent oldExamStudent = listExamStudent.get(i);
            ExamStudent beforeExamStudent = ExamStudent.builder().id(oldExamStudent.getId()).stuNo(oldExamStudent.getStuNo())
                    .seatNumber(oldExamStudent.getSeatNumber()).build();
            before.add(beforeExamStudent);
            ExamStudent afterExamStudent = ExamStudent.builder().id(oldExamStudent.getId()).stuNo(oldExamStudent.getStuNo())
                    .seatNumber(i + 1).build();
            after.add(afterExamStudent);
            examStudentMapper.updateByPrimaryKeySelective(afterExamStudent);
        }
        
        // 添加日志
        String instruction = "考试安排-考试学生-自动排号";
        sysLogService.saveSysLogBySysUser(before, after, instruction);
    }
}
